package wiki.xsx.core.jmeter.core.preprocessor;

import wiki.xsx.core.jmeter.core.util.JmeterOptional;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.jmeter.modifiers.UserParameters;
import org.apache.jmeter.modifiers.gui.UserParametersGui;
import org.apache.jmeter.processor.PreProcessor;
import org.apache.jmeter.testelement.TestElement;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 默认用户参数前置处理器
 *
 * @author xsx
 * @date 2022/9/2
 * @since 1.8
 * <p>
 * Copyright (c) 2022 xsx All Rights Reserved.
 * easy-jmeter is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * </p>
 */
@Data
@Accessors(chain = true)
public class JmeterDefaultUserParametersPreProcessor implements JmeterPreProcessor {

    /**
     * 测试类名称
     */
    public static final String TEST_CLASS_NAME = UserParameters.class.getName();
    /**
     * GUI类名称
     */
    public static final String GUI_CLASS_NAME = UserParametersGui.class.getName();

    /**
     * 处理器名称
     */
    private String name;
    /**
     * 处理器注释
     */
    private String comment;
    /**
     * 是否每次迭代更新
     */
    private Boolean isPerIteration;
    /**
     * 用户参数列表
     */
    private List<JmeterUserParameter> userParameters = new ArrayList<>(10);

    /**
     * 无参构造
     */
    private JmeterDefaultUserParametersPreProcessor() {
    }

    /**
     * 获取用户参数前置处理器实例
     *
     * @return 返回用户参数前置处理器实例
     */
    public static JmeterDefaultUserParametersPreProcessor getInstance() {
        return new JmeterDefaultUserParametersPreProcessor();
    }

    /**
     * 初始化用户参数容量
     *
     * @param initialCapacity 初始容量
     * @return 返回用户参数前置处理器
     */
    public JmeterDefaultUserParametersPreProcessor initialUserParameterCapacity(int initialCapacity) {
        this.userParameters = new ArrayList<>(initialCapacity);
        return this;
    }

    /**
     * 添加用户参数
     *
     * @param userParameters 用户参数
     * @return 返回用户参数前置处理器
     */
    public JmeterDefaultUserParametersPreProcessor addUserParameter(JmeterUserParameter... userParameters) {
        // 如果用户参数列表为空，则初始化用户参数列表
        if (this.userParameters == null) {
            // 初始化用户参数列表
            this.userParameters = new ArrayList<>(10);
        }
        // 添加用户参数
        Collections.addAll(this.userParameters, userParameters);
        // 返回用户参数前置处理器
        return this;
    }

    /**
     * 创建前置处理器
     *
     * @return 返回前置处理器
     */
    @Override
    public PreProcessor create() {
        // 创建用户参数前置处理器
        UserParameters userParameters = new UserParameters();
        // 设置测试类名称
        userParameters.setProperty(TestElement.TEST_CLASS, TEST_CLASS_NAME);
        // 设置GUI类名称
        userParameters.setProperty(TestElement.GUI_CLASS, GUI_CLASS_NAME);
        // 设置启用
        userParameters.setEnabled(true);
        // 设置处理器名称
        userParameters.setName(JmeterOptional.ofNullable(this.name).orElse("用户参数"));
        // 设置处理器注释
        userParameters.setComment(JmeterOptional.ofNullable(this.comment).orElse(""));
        // 设置是否每次迭代更新
        userParameters.setPerIteration(JmeterOptional.ofNullable(this.isPerIteration).orElse(Boolean.FALSE));
        // 设置用户参数列表
        JmeterOptional.ofNullable(this.userParameters).ifPresent(v -> this.initUserParameters(v, userParameters));
        // 返回用户参数前置处理器
        return userParameters;
    }

    /**
     * 初始化用户参数
     *
     * @param parameters     用户参数列表
     * @param userParameters 用户参数前置处理器
     */
    private void initUserParameters(List<JmeterUserParameter> parameters, UserParameters userParameters) {
        // 创建参数名称列表
        List<String> names = new ArrayList<>(parameters.size());
        // 创建参数值列表
        List<List<Object>> values = new ArrayList<>(parameters.size());
        // 添加参数名称与参数值列表
        parameters.stream().filter(p -> p.getName() != null && p.getValues() != null).forEach(
                parameter -> {
                    // 添加参数名称
                    names.add(parameter.getName());
                    // 添加参数值列表
                    values.add(parameter.getValues());
                }
        );
        // 设置参数名称
        userParameters.setNames(names);
        // 设置参数值列表
        userParameters.setThreadLists(values);
    }
}
